package com.alogic.pipeline.loader;

import com.alogic.load.Loader;
import com.alogic.pipeline.DefaultPipeline;
import com.alogic.pipeline.Pipeline;
import com.anysoft.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.w3c.dom.Document;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 从classpath加载
 * @since 1.6.13.35 [20210218 duanyy] <br>
 *
 * @version 1.6.14.11 [20210524 duanyy] <br>
 * - 修复jar搜索问题 <br>
 */
public class FromClasspath extends Loader.Abstract<Pipeline>{
    /**
     * 类路径
     */
    protected String path;

    /**
     * 引导类
     */
    protected String bootstrap;

    protected String suffix = ".xml";

    /**
     * 预加载的对象
     */
    private Map<String, Pipeline> pipelines = new ConcurrentHashMap<String,Pipeline>();

    @Override
    public Pipeline load(String id, boolean cacheAllowed) {
        return pipelines.get(id);
    }

    @Override
    public void configure(Properties p){
        super.configure(p);

        path = PropertiesConstants.getString(p, "path", "",true);
        bootstrap = PropertiesConstants.getString(p, "bootstrap", "",true);
        suffix = PropertiesConstants.getString(p,"suffix",suffix,true);

        if (StringUtils.isNotEmpty(path) && StringUtils.isNotEmpty(bootstrap)){
            ClassLoader cl = Settings.getClassLoader();
            try {
                Class<?> clazz = cl.loadClass(bootstrap);
                scanResource(path,clazz);
            }catch (Exception ex){
                LOG.error("Can not load class " + bootstrap);
                LOG.error(ExceptionUtils.getStackTrace(ex));
            }
        }else{
            LOG.error("Can not scan class path,because the path or the bootstap is null");
        }
    }

    /**
     * 从指定的CLASSPATH路径中扫描资源
     *
     * @param home 路径
     * @param clazz 引导类
     */
    protected void scanResource(String home, Class<?> clazz) {
        URL url = clazz.getResource(home);

        if (url == null){
            LOG.warn(String.format("Can not find resource path:%s",home));
            return ;
        }

        if(url.toString().startsWith("file:")){
            File file;
            try {
                file = new File(url.toURI());
            } catch (URISyntaxException e) {
                LOG.error("Can not scan home:" + path,e);
                LOG.error(ExceptionUtils.getStackTrace(e));
                return;
            }
            LOG.info(String.format("Scan event processor in %s",url.toString()));
            scanFileSystem(home,file,clazz);
        }
        else{
            if (url.toString().startsWith("jar:")){
                JarFile jfile;
                try {
                    String jarUrl = url.toString();
                    jarUrl = jarUrl.substring(9, jarUrl.indexOf('!'));
                    jfile = new JarFile(jarUrl);
                } catch (IOException e) {
                    LOG.error("Can not scan home:" + path,e);
                    LOG.error(ExceptionUtils.getStackTrace(e));
                    return;
                }
                LOG.info(String.format("Scan event processor in %s",url.toString()));
                scanJar(home,jfile,clazz);
            }
        }
    }

    /**
     * 扫描指定的Jar
     * @param home Jar中的路径
     * @param jarFile jarFile
     */
    protected void scanJar(String home, JarFile jarFile,Class<?> clazz) {
        Enumeration<JarEntry> files = jarFile.entries();
        while (files.hasMoreElements()) {
            JarEntry entry = files.nextElement();
            String name = entry.getName();
            if (name.endsWith(suffix)) {
                int end = name.lastIndexOf('/');
                if (end > 0) {
                    String path = '/' + name.substring(0, end);
                    if (path.equals(home)) {
                        // got
                        LOG.info(String.format("Found pipeline: %s", entry.getName()));
                        String classPath = "/" + name;
                        ;
                        InputStream in = null;
                        try {
                            in = clazz.getResourceAsStream(classPath);
                            Document doc = XmlTools.loadFromInputStream(in);
                            Factory<Pipeline> f = new Factory<Pipeline>();
                            Pipeline p = f.newInstance(
                                    doc.getDocumentElement(), Settings.get(),
                                    "module", DefaultPipeline.class.getName());
                            if (p != null && !p.isNull()) {
                                pipelines.put(p.getId(), p);
                            } else {
                                LOG.warn(String.format("Pipeline id is not ,ignored:%s",
                                        XmlTools.node2String(doc.getDocumentElement())));
                            }
                        } catch (Exception ex) {
                            LOG.error("Can not load file:" + classPath);
                            LOG.error(ExceptionUtils.getStackTrace(ex));
                        } finally {
                            IOTools.close(in);
                        }
                    }
                }
            }
        }
    }

    /**
     * 扫描文件系统
     * @param path
     * @param file
     * @param clazz
     */
    protected void scanFileSystem(String path, File file,Class<?> clazz) {
        File[] files = file.listFiles();
        for (File item:files){
            if (item.getName().endsWith(suffix)){
                LOG.info(String.format("Found pipeline : %s",item.getName()));
                InputStream in = null;
                try {
                    in = new FileInputStream(item);
                    Document doc = XmlTools.loadFromInputStream(in);
                    Factory<Pipeline> f = new Factory<Pipeline>();
                    Pipeline p =  f.newInstance(
                            doc.getDocumentElement(),Settings.get(),
                            "module", DefaultPipeline.class.getName());
                    String id = p.getId();
                    if (StringUtils.isNotEmpty(id)){
                        pipelines.put(id,p);
                    }else{
                        LOG.warn(String.format("Pipeline id is not ,ignored:%s",
                                XmlTools.node2String(doc.getDocumentElement())));
                    }
                }catch (Exception ex){
                    LOG.error("Can not load file:" + item.getPath());
                    LOG.error(ExceptionUtils.getStackTrace(ex));
                }finally{
                    IOTools.close(in);
                }
            }
        }
    }
}